﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using jaysJourney_v01.Common;
using jaysJourney_v01.Utils;
using System.Diagnostics;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Media;
using jaysJourney_v01.Utils.Levels;
using jaysJourney_v01.Utils.Blocks.Interactive;

namespace jaysJourney_v01.States
{
    class PlayState : State
    {
        /**
         * MEMBER VARIABLES
         * */

        // Common

        private SpriteBatch mainSpriteBatch;

        private int delayCounter = (int)CONST.DELAY_TIME;
        private bool isStartDelay = true;

        private bool jayHasWon = false;

        // Util

        private Collider worldCollider;

        private Texture2D overlayReady;
        private Texture2D overlaySet;

        //loadingscreen

        private bool loading;
        private Texture2D loadingscreen;

        // World

        private LevelBuilder level;
        private Level lvlData;

        private Vector2 curZero;

        private Texture2D background;
        private Texture2D parallaxBG;

        public InteractiveBlockManager interactiveBlocks;

        // Jay

        public Jay jay;

        // Camera

        private Vector2 camSpeed;
        private float camtrigger = 0;
        private int activeCamX = 0;
        //private int activeCamY = 0;

        // Sound

        private Song bgMusic;
        private bool isMusicPlaying = false;

        // LevelTime

        private int curTime = 0;
        private bool stoptimer = false;
        

        /**
         * METHODS
         * */

        // Constructor

        public PlayState(String name, StateManager stateManager) : base(name, stateManager) {} // empty constructor

        // init state

        public override void initialize()
        {
            mainSpriteBatch = this.parentSpriteBatch;
            this.level = new LevelBuilder(this, this.mainSpriteBatch);
            this.jay = new Jay(level.StartPoint * CONST.BLOCKSIZE, this);
            this.worldCollider = new Collider(this.level, CONST.BLOCKSIZE, jay);
            this.interactiveBlocks = new InteractiveBlockManager(this.mainSpriteBatch, this);
            this.loadingscreen = this.getGame().Content.Load<Texture2D>(@"Textures\Preloader\loadingscreen");


            this.curZero = new Vector2(this.jay.newPosition.X - (this.getGame().GraphicsDevice.Viewport.Width / 2), this.jay.newPosition.Y - (this.getGame().GraphicsDevice.Viewport.Height / 2));
            checkBorders();

            this.background = this.getGame().Content.Load<Texture2D>(@"Textures\Settings\Setting1\background");
            this.parallaxBG = this.getGame().Content.Load<Texture2D>(@"Textures\Settings\Setting1\parallax_bg");
            this.camSpeed = new Vector2(0, 0);
            this.bgMusic = this.getGame().Content.Load<Song>(@"Audio\Music\anamanguchi_mess_loopable");

            this.overlayReady = this.getGame().Content.Load<Texture2D>(@"Textures\Interface\overlay_ready");
            this.overlaySet = this.getGame().Content.Load<Texture2D>(@"Textures\Interface\overlay_set");
        }

        // MAIN UPDATE LOOP

        public override void update(GameTime gameTime)
        {
            // update start delayad
            if (this.isStartDelay)
            {
                this.loading = false;
                this.delayCounter -= gameTime.ElapsedGameTime.Milliseconds;

                if (this.delayCounter <= 0)
                {
                    this.isStartDelay = false;
                    this.jay.setGameLock(false);
                }
            }

            // update blocks
            this.interactiveBlocks.update(gameTime);

            // update jays position
            jay.update(gameTime);

            // update camera
            if(!this.jayHasWon)this.updateCamera(gameTime);

            // Audio
            if (!isMusicPlaying)
            {
                MediaPlayer.Volume = this.getStateManager().getGame().volumeLevel*1.0f; //+specific volume parameter(float)
                MediaPlayer.IsRepeating = true;
                MediaPlayer.Play(this.bgMusic);
                isMusicPlaying = true;
            }

            // input
            if (Keyboard.GetState().IsKeyDown(Keys.Escape) || GamePad.GetState(PlayerIndex.One).IsButtonDown(Buttons.Start))
            {
                this.musicOff();

                if (getStateManager().getLastActiveState() == "editorState") this.getStateManager().setActiveState("editorState");
                else this.getStateManager().setActiveState("pauseState");
            }

            // check goal
            if (reachedGoal(jay) && !this.jayHasWon && !this.jay.isJayDead())
            {
                level.goal.triggerBase();
                this.jayHasWon = true;
                this.musicOff();
                this.jay.winJay();
                this.delayCounter = (int)CONST.DELAY_TIME;
            }
            
            if(this.jayHasWon)
            {
                if (!this.stoptimer) this.stoptimer = true;
                if (loading)
                {
                    this.jayHasWon = false;
                    loading = false;
                    this.getLvlData().setNumStarts(this.getLvlData().getNumStarts() + 1);
                    WebSaver.worldUpdate(this.getLvlData().getLevel(), this.getLvlData().getNumStarts(), this.getLvlData().getNumRates(), this.getLvlData().getCurRating(), this.getLvlData().getBestTime(), this.getLvlData().getRecordHolder(), this.getLvlData().getAvgBestTime());
                    this.getStateManager().setActiveState("winState");
                    ((WinState)this.getStateManager().getState("winState")).updateRanking();
                }
                this.delayCounter -= gameTime.ElapsedGameTime.Milliseconds;

                if (this.delayCounter <= 0)
                {
                    this.jayHasWon = false;

                    if (getStateManager().getLastActiveState() == "editorState")
                    {
                        ((EditorState)getStateManager().getState("editorState")).setBesttime(curTime);
                        ((EditorState)getStateManager().getState("editorState")).testSuccessful = true;
                        this.getStateManager().setActiveState("editorState");
                    }
                    else
                    {
                        
                        loading = true;
                    }
                }
            }

            // update Timer
            if (!this.isStartDelay && !this.jayHasWon && !this.stoptimer) this.curTime += gameTime.ElapsedGameTime.Milliseconds;
        }

        // MAIN DRAW METHOD

        public override void draw(GameTime gameTime)
        {
            // draw background
            this.mainSpriteBatch.Draw(this.background, new Rectangle(0, 0, this.getGame().GraphicsDevice.Viewport.Width, this.getGame().GraphicsDevice.Viewport.Height), new Rectangle(0, 0, this.getGame().GraphicsDevice.Viewport.Width, this.getGame().GraphicsDevice.Viewport.Height), Color.White, 0, new Vector2(0, 0), SpriteEffects.None, 1);

            // draw parallax bg
            this.mainSpriteBatch.Draw(this.parallaxBG, new Vector2(-((curZero.X / 2) % (this.parallaxBG.Width)), (this.level.YCOUNT * CONST.BLOCKSIZE - curZero.Y + this.getGame().GraphicsDevice.Viewport.Height) / 2 - parallaxBG.Height), Color.White);
            this.mainSpriteBatch.Draw(this.parallaxBG, new Vector2(-((curZero.X / 2) % (this.parallaxBG.Width)) + this.parallaxBG.Width, (this.level.YCOUNT * CONST.BLOCKSIZE - curZero.Y + this.getGame().GraphicsDevice.Viewport.Height) / 2 - parallaxBG.Height), Color.White);            
           
            // draw world
            this.level.drawWorld(gameTime, this.curZero);

            // draw Interactive Blocks
            this.interactiveBlocks.draw(gameTime, this.curZero);

            // draw bob
            this.jay.Draw(gameTime, this.curZero);  
          
            // draw timer
            this.mainSpriteBatch.DrawString(this.getGame().textFont, Formater.formatTime(this.curTime), new Vector2(this.getGame().GraphicsDevice.Viewport.Width - 120, 20), Color.White);

            // draw delay time
            /**if (this.isStartDelay)
            {
                this.mainSpriteBatch.DrawString(this.getGame().textFont, Formater.formatTime(this.delayCounter), new Vector2((this.getGame().GraphicsDevice.Viewport.Width / 2) - 120, (this.getGame().GraphicsDevice.Viewport.Height / 2) - 20), Color.Red);
            }*/
            if(this.isStartDelay)
            {
                if (this.delayCounter > 1000)
                {
                    this.mainSpriteBatch.Draw(this.overlayReady, new Rectangle((this.getGame().GraphicsDevice.Viewport.Width / 2) - this.overlayReady.Width / 2, (this.getGame().GraphicsDevice.Viewport.Height / 2) - this.overlayReady.Height / 2, this.overlayReady.Width, this.overlayReady.Height), new Rectangle(0, 0, this.overlayReady.Width, this.overlayReady.Height), Color.White);
                }
                else if (this.delayCounter > 0 && this.delayCounter <= 1000)
                {
                    this.mainSpriteBatch.Draw(this.overlaySet, new Rectangle((this.getGame().GraphicsDevice.Viewport.Width / 2) - this.overlaySet.Width / 2, (this.getGame().GraphicsDevice.Viewport.Height / 2) - this.overlayReady.Height / 2, this.overlaySet.Width, this.overlaySet.Height), new Rectangle(0, 0, this.overlaySet.Width, this.overlaySet.Height), Color.White);
                }
            }
            if (loading) this.mainSpriteBatch.Draw(this.loadingscreen, new Rectangle((this.getGame().GraphicsDevice.Viewport.Width / 2) - this.loadingscreen.Width / 2, (this.getGame().GraphicsDevice.Viewport.Height / 2) - this.loadingscreen.Height / 2, this.loadingscreen.Width, this.loadingscreen.Height), new Rectangle(0, 0, this.loadingscreen.Width, this.loadingscreen.Height), Color.White);

        }

        public override void onEnterState()
        {
            this.stoptimer = false;
            this.musicOn();
            this.isStartDelay = true;
            this.delayCounter = (int)CONST.DELAY_TIME;
            this.jay.setGameLock(true);
        }

        // get mainSpriteBatch

        public SpriteBatch getMainSpriteBatch()
        {
            return this.mainSpriteBatch;
        }

        // get collider

        public Collider getCollider()
        {
            return this.worldCollider;
        }

        // get curLevel

        public LevelBuilder getLevel()
        {
            return this.level;
        }

        // update camera

        private void updateCamera(GameTime gameTime)
        {
            float tpf = (float)gameTime.ElapsedGameTime.Milliseconds/1000;
            Vector2 jaypos = new Vector2(this.jay.newPosition.X - curZero.X, this.jay.newPosition.Y - curZero.Y);
            int width = getGame().GraphicsDevice.Viewport.Width;
            int height = getGame().GraphicsDevice.Viewport.Height;
 
            if (jaypos.X> width*1/5&&jay.curRunningSpeed > 0 && camtrigger <= 1) { camtrigger += tpf; }
            else if (jaypos.X< level.XCOUNT*CONST.BLOCKSIZE -width*1/5&&jay.curRunningSpeed < 0 && camtrigger >= -1) { camtrigger -= tpf; }
            //if (jaypos.Y > height / 3 && jay.curFallSpeed < 0) {this.activeCamY = 1; }
            //else if (jaypos.Y < height / 3 && jay.curFallSpeed > 0) {this.activeCamY = 2; }

            int minCamSpeed = 5;
            float acc = 5 * tpf;
            if (camtrigger > 0.7f)
            {
                activeCamX = 1;
            }
            if (camtrigger < -0.7f)
            {
                activeCamX = 2;
            }
            if (activeCamX == 1)
            {
                if ((camSpeed.X < minCamSpeed)  && jaypos.X > width /2)
                {
                    camSpeed.X += acc;
                }
                else if (jaypos.X < width /2 && jaypos.X > width * 2 / 5)
                {
                    float diff = (width / 2 - jaypos.X)/width*10;
                    if (diff < 0) diff = 0;
                    if (diff > 1) diff = 1;
                    camSpeed.X = minCamSpeed * (1 - diff) + jay.curRunningSpeed * diff;
                }
                else if (jaypos.X < width * 2 / 5)
                {
                    camSpeed.X = jay.curRunningSpeed;
                }            
            }
            if (activeCamX == 2)
            {
                if ((camSpeed.X > -minCamSpeed) && jaypos.X < width / 2)
                {
                    camSpeed.X -= acc;
                }
                else if (jaypos.X > width / 2 && jaypos.X < width * 3 / 5)
                {
                    float diff = (jaypos.X - width / 2) / width * 10;
                    if (diff < 0) diff = 0;
                    if (diff > 1) diff = 1;
                    camSpeed.X = minCamSpeed * (diff-1) + jay.curRunningSpeed * diff;
                }
                else if (jaypos.X > width * 3 / 5)
                {
                    camSpeed.X = jay.curRunningSpeed;
                } 

            }
            if (jaypos.Y < height * 2 / 5 && jay.curFallSpeed > 0)
            {
                camSpeed.Y = -jay.curFallSpeed;
            }
            if (jaypos.Y > height * 2 / 5 && jaypos.Y < height * 3 / 5) camSpeed.Y = 0;
            if (jaypos.Y > height * 3 / 5 && jay.curFallSpeed < 0)
            {
                camSpeed.Y = -jay.curFallSpeed;
            }
            curZero.X += camSpeed.X;
            curZero.Y += camSpeed.Y;

            checkBorders();
        }

        // Check if Jay reached goal

        public bool reachedGoal(Jay jay)
        {
            Rectangle jayRect = new Rectangle((int)jay.newPosition.X, (int)jay.newPosition.Y, 32, 64);            
            Rectangle goalRect = new Rectangle((int)this.level.Goal.X * CONST.BLOCKSIZE, (int)this.level.Goal.Y * CONST.BLOCKSIZE, 32, 64);            

            if (goalRect.Intersects(jayRect)) return true;
            else return false;
        }

        // Checking for worldborders

        private void checkBorders()
        {
            if (this.curZero.X < 0) this.curZero.X = 0;
            else if (this.curZero.X > ((this.level.XCOUNT) * CONST.BLOCKSIZE) - this.getGame().GraphicsDevice.Viewport.Width) this.curZero.X = ((this.level.XCOUNT) * CONST.BLOCKSIZE) - this.getGame().GraphicsDevice.Viewport.Width;
            if (this.curZero.Y < 0) this.curZero.Y = 0;
            else if (this.curZero.Y > ((this.level.YCOUNT) * CONST.BLOCKSIZE) - this.getGame().GraphicsDevice.Viewport.Height) this.curZero.Y = ((this.level.YCOUNT) * CONST.BLOCKSIZE) - this.getGame().GraphicsDevice.Viewport.Height;
        }

        // Reset current world

        public void resetLevel()
        {
            this.level.splitStringtoWorld(); // reload world
            this.background = this.getGame().Content.Load<Texture2D>(@"Textures\Settings\Setting"+level.SETTING+@"\background");
            this.parallaxBG = this.getGame().Content.Load<Texture2D>(@"Textures\Settings\Setting" + level.SETTING + @"\parallax_bg");
            this.jay = new Jay(level.StartPoint * CONST.BLOCKSIZE, this); // reset jay

            // reset cam
            this.curZero = new Vector2(this.jay.newPosition.X - (this.getGame().GraphicsDevice.Viewport.Width/2), this.jay.newPosition.Y - (this.getGame().GraphicsDevice.Viewport.Height / 2));
            checkBorders();
            this.camSpeed = new Vector2(0, 0);
            this.activeCamX = 0;
            //this.activeCamY = 0;
            this.camtrigger = 0;

            // reset timer
            this.curTime = 0;
        }

        // set cur lvlData

        public void setLvlData(Level lvl)
        {
            this.lvlData = lvl;
        }

        // get cur lvlData

        public Level getLvlData()
        {
            return this.lvlData;
        }

        // get bestTime

        public int getCurTime()
        {
            return this.curTime;
        }

        // toggleMusic

        public void musicOn()
        {
            MediaPlayer.Play(this.bgMusic);
        }

        public void musicOff()
        {
            MediaPlayer.Stop();
        }

        public bool getStartDelay()
        {
            return this.isStartDelay;
        }
    }
}
